﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
using System.Threading;
using System.Collections.Concurrent;
using System.Windows;

namespace SynchronizedPlayer {
	class NetSender {
		IPEndPoint _ipEndPoint;
		Socket _sender;
		bool _sending = false;
		ConcurrentQueue<Command> _outQueue = new ConcurrentQueue<Command>();

		public NetSender(string IP, int port) {
			_ipEndPoint = new IPEndPoint(IPAddress.Parse(IP), port);
			_sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
		}
		
		public void Enqueue(Command cmd) {
			try {
				_outQueue.Enqueue(cmd);
				if (!_sending) {
					Command c;
					if (_outQueue.TryDequeue(out c)) {
						Send(c);
					}
				}
			} catch (Exception e) {
				MessageBox.Show("Sender Enqueue" + "Check your internet connection and settings (ip address and port)", "Connection error!",
					MessageBoxButton.OK, MessageBoxImage.Error);
			}
		}

		public void Send(Command cmd) {
			Debug.WriteLine("Send");
			_sending = true;
			_sender.BeginConnect(_ipEndPoint, connectCallback, new Tuple<Socket, Command>(_sender, cmd));
		}
		
		private void connectCallback(IAsyncResult res) {
			Debug.WriteLine("connectCallback");
			Socket client = null;
			try {
				client = (Socket)((Tuple<Socket, Command>)res.AsyncState).Item1;
				Command sendcmd = (Command)(((Tuple<Socket, Command>)res.AsyncState).Item2);
				client.EndConnect(res);
				var bytes = sendcmd.ToByteArray();
				_sender.BeginSend(bytes, 0, bytes.Length, SocketFlags.None, sendCallback, _sender);
			} catch (Exception e) {
				MessageBox.Show(e.Message + "Check your internet connection and settings (ip address and port)", "Connection error!",
					MessageBoxButton.OK, MessageBoxImage.Error);
			}
		}

		private void sendCallback(IAsyncResult res) {
			Debug.WriteLine("sendCallback");
			try {
				Socket handler = (Socket)(res.AsyncState);
				handler.EndSend(res);
				handler.BeginDisconnect(true, disconnectCallback, handler);
			} catch (Exception e) {
				MessageBox.Show(e.Message + "Check your internet connection and settings (ip address and port)", "Connection error!",
					MessageBoxButton.OK, MessageBoxImage.Error);
			}
		}

		private void disconnectCallback(IAsyncResult res) {
			Debug.WriteLine("disconnectCallback");
			try {
				Socket handler = (Socket)(res.AsyncState);
				handler.EndDisconnect(res);
				_sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
				
				Command c;
				if (_outQueue.TryDequeue(out c)) {
					_sender.BeginConnect(_ipEndPoint, connectCallback, new Tuple<Socket, Command>(_sender, c));
				} else {
					_sending = false;
				}
			} catch (Exception e) {
				MessageBox.Show(e.Message + "Check your internet connection and settings (ip address and port)", "Connection error!",
					MessageBoxButton.OK, MessageBoxImage.Error);
			}

		}

		public void Dispose() {
			if (_sender != null) {
				_sender.Close();
			}
		}
	}
}
